#include <sys/time.h>

#include <evpp/udp/udp_server.h>
#include <evpp/udp/udp_message.h>
#include <evpp/event_loop.h>
#include <evpp/event_loop_thread_pool.h>

#include "ikcp.h"

IKCPCB *p_kcp = nullptr;
evpp_socket_t sockfd;
struct sockaddr remote_addr;

/* get system time */
static inline void itimeofday(long *sec, long *usec)
{
	struct timeval time;
	gettimeofday(&time, NULL);
	if (sec) *sec = time.tv_sec;
	if (usec) *usec = time.tv_usec;
}

/* get clock in millisecond 64 */
static inline IINT64 iclock64(void)
{
    long s, u;
    IINT64 value;
    itimeofday(&s, &u);
    value = ((IINT64)s) * 1000 + (u / 1000);
    return value;
}

static inline IUINT32 iclock()
{
    return (IUINT32)(iclock64() & 0xfffffffful);
}

int udp_output(const char *buf, int len, ikcpcb *kcp, void *user)
{
	evpp::udp::SendMessage(sockfd, &remote_addr, buf, len);
	return 0;
}

void udp_input(evpp::EventLoop *loop, evpp::udp::MessagePtr msg)
{
	sockfd = msg->sockfd();
	remote_addr = *msg->remote_addr();

	ikcp_input(p_kcp, msg->data(), msg->size());
	static char buf[1024 * 10];
	int n = ikcp_recv(p_kcp, buf, sizeof buf);
	if (n > 0) {
		ikcp_send(p_kcp, buf, n);
		std::cout << std::string(buf, n) << std::endl;
	}
}

void init_kcp()
{
    p_kcp = ikcp_create(123456, (void*)11);
    p_kcp->output = udp_output;
    ikcp_nodelay(p_kcp, 1, 2, 1, 1);
}

void update_kcp()
{
	uint32_t cur_clock = iclock();
	ikcp_update(p_kcp, cur_clock);
}

int main(void)
{
	init_kcp();
	evpp::udp::Server udp_server;
	udp_server.SetMessageHandler(udp_input);
	udp_server.Init(29099);
	udp_server.Start();

	evpp::EventLoop loop;
	loop.RunEvery(evpp::Duration(10), update_kcp);
	std::shared_ptr<evpp::EventLoopThreadPool> tpool(new evpp::EventLoopThreadPool(&loop, 0));
	tpool->Start(true);
	udp_server.SetEventLoopThreadPool(tpool);
	loop.Run();
	udp_server.Stop(true);
	tpool->Stop(true);
	return 0;
}
